﻿// This File is part of Innovation Foundry's "QualityHelper Project"
//
// Copyright  2009 Innovation Foundry Pty Ltd
// All rights reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA  02110-1301 USA
// or alternatively it may be downloaded from <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Windows.Browser;
using System.Windows.Interop;
using System.Collections.ObjectModel;

using InnovationFoundry.QualityHelper.Utilities;
using InnovationFoundry.QualityHelper.Silverlight.BLL.Objects;
using InnovationFoundry.QualityHelper.Silverlight.Assets.Resources;
using InnovationFoundry.QualityHelper.Silverlight.Controls.Diagram;
using InnovationFoundry.QualityHelper.Silverlight.Controls.Zoom;

namespace InnovationFoundry.QualityHelper.Silverlight.Controls.CoverFlow
{
    public partial class CoverFlowControl : UserControl
    {

        public delegate void MenuIndexChangedHandler(object sender, SelectedMenuItemArgs e);
        public event MenuIndexChangedHandler MenuItemClicked;
        public delegate void ProcedureClickedHandler(object sender, ProcedureClickedArgs e);
        public event ProcedureClickedHandler ProcedureClicked;
        public delegate void ProcedureDoubleClickedHandler(object sender, ProcedureClickedArgs e);
        public event ProcedureDoubleClickedHandler ProcedureDoubleClicked;
        public delegate void CoverFlowChanged(object sender, object diagramID);
        public event CoverFlowChanged CoverFlowChangedEvent;
        RingBufferObservableWithID<DiagramObject> diagramCache = new RingBufferObservableWithID<DiagramObject>(100);
        List<string> diagramsToDraw = new List<string>();
        const int MAXDIAGRAMS = 7;
        const string DELIMITER = ",";
        const string PREFIX = "*++";
        const string SUFFIX = "++*";
        DiagramObject[] diagrams;
        DiagramObject rootDiagram;
        private int selectedIndex = 0;

        public CoverFlowControl()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(CoverFlowControl_Loaded);
            Application.Current.Host.Content.Resized += new EventHandler(Content_Resized);
            //zoom = new Zoomify();
            //LayoutRoot.Children.Add(zoom);
        }

        private void Content_Resized(object sender, EventArgs args)
        {
            if (double.Equals(double.NaN,Application.Current.Host.Content.ActualWidth) || double.Equals(double.NaN, Application.Current.Host.Content.ActualWidth))
                return;
            this.MaxWidth = (Application.Current.Host.Content.ActualWidth < 640) ? 640.0 : Application.Current.Host.Content.ActualWidth;
            this.MaxHeight = (Application.Current.Host.Content.ActualHeight < 480) ? 480.0-150.0 : Application.Current.Host.Content.ActualHeight - 150;
            this.Width = this.MaxWidth;
            this.Height = this.MaxHeight;
            diagramHeight = Application.Current.Host.Content.ActualHeight - 150;
            diagramHeight = (diagramHeight < 240) ? 240 : diagramHeight;
            diagramWidth = Application.Current.Host.Content.ActualWidth / 7 * 3;
            diagramWidth = (diagramWidth < 320) ? 320 : diagramWidth;
            videoflow.Width = diagramWidth;
            videoflow_reflection.Width = diagramWidth;
            videoflow.Height = diagramHeight;
            videoflow_reflection.Height = diagramHeight;
            videoflow.SetValue(Canvas.LeftProperty, this.Width / 2 - diagramWidth / 2);
            videoflow_reflection.SetValue(Canvas.LeftProperty, this.Width / 2 - diagramWidth / 2);
            videoflow_reflection.SetValue(Canvas.TopProperty, diagramHeight);
            RefreshDiagrams();
        }

        public string CurrentDiagramName {
            get 
            {
                if (diagrams.Length > selectedIndex)
                    return diagrams[selectedIndex].Title;
                else return "";
            }
        }

        public void AddDiagramToCache(DiagramObject diagram)
        {
            if (diagram == null)
                return;
            if (!DiagramExistsInCache(diagram))
                diagramCache.AddToRing(diagram);
            if (diagram.ID == null)
                rootDiagram = diagram;
        }

        //Check if it exists (if it does, move to top of cache)
        public bool DiagramExistsInCache(object id)
        {
            int index = diagramCache.GetFirstItemIndexByID(id);
            if (index == -1)
            {
                //this will allow us to know whether we should check in db
                return false;
            }
            else
            {
                refreshCache(index);
                return true;
            }
        }

        private void refreshCache(int diagramIndex) 
        {
            //dont check, just update the cache
            DiagramObject diagram = diagramCache[diagramIndex];
            diagramCache.RemoveAt(diagramIndex);
            diagramCache.AddToRing(diagram);
        }

        public bool ProcedureExistsInDiagram(object procedureID, object diagramID)
        {
            int index = diagramCache.GetFirstItemIndexByID(diagramID);
            if (index == -1)
                return false;
            DiagramObject diagram = diagramCache[index];
            if (diagram == null)
                return false;
            if (diagram.DiagramID == null && procedureID == null)
                return true;
            if (diagram.DiagramID != null && procedureID != null && diagram.DiagramID.IndexOf(PREFIX + (procedureID as string) + SUFFIX) > -1)
                return true;
            if (diagram.DiagramID == null)
            {
                if (diagram.Procedures.Count(p => p.ProcedureID == (string)procedureID) > 0)
                {
                    refreshCache(index);
                    return true;
                }
            }
            return false;
        }

        public DiagramObject ProcedureExistsInDiagrams(object procedureID)
        {
            for (int i = 0; i < diagramCache.Count; i++)
            {
                if (diagramCache[i].DiagramID != null && diagramCache[i].DiagramID.IndexOf(PREFIX + (procedureID as string) + SUFFIX) > -1)
                {
                    DiagramObject toReturn = diagramCache[i];
                    refreshCache(i);
                    return toReturn;
                }
                if (diagramCache[i].DiagramID == null)
                {
                    if (procedureID == null || (diagramCache[i].Procedures.Count(p => p.ProcedureID == (string)procedureID) > 0))
                    {
                        DiagramObject toReturn = diagramCache[i];
                        refreshCache(i);
                        return toReturn;
                    }
                }
            }
            return null;
        }

        public void ClearDiagrams()
        {
            diagramsToDraw.Clear();
        }

        public void PrependDiagram(string prependageID, string parentID, bool replaceParent)
        {
            List<string> prependageIDs = new List<string>();
            prependageIDs.Add(prependageID);
            PrependDiagrams(prependageIDs, parentID, replaceParent);
        }
        
        public void PrependDiagrams(List<string> prependageIDs, string parentID,  bool replaceParent) 
        {
            int positionOfParentToDraw = ProcedurePositionInDiagramsToDraw(parentID);
            int positionOfFirstPrependage = ProcedurePositionInDiagramsToDraw(prependageIDs[0]);
            for (int i = 0; i < prependageIDs.Count; i++)
            {
                DiagramObject d = ProcedureExistsInDiagrams(prependageIDs[i]);
                if (d != null)
                    prependageIDs[i] = d.DiagramID;
            }
            for (int i = (replaceParent) ? positionOfParentToDraw : positionOfParentToDraw - 1; i > -1; i--)
                diagramsToDraw.RemoveAt(i);
            //if it is already there forget it
            if (diagramsToDraw.Count > 0 && diagramsToDraw[0] == prependageIDs[0])
                return;
            diagramsToDraw.InsertRange(0, prependageIDs);
            //now remove any duplicate references after parentID and all childnodes of duplicate
            if (prependageIDs.Count < 1)
                return;
            //start counting here
            positionOfParentToDraw = (replaceParent) ? 0 : 1;
            positionOfParentToDraw += prependageIDs.Count;
            int indexOfDuplicate = -1;
            for (int i = positionOfParentToDraw; i < diagramsToDraw.Count; i++)
            {
                if (prependageIDs[0] == diagramsToDraw[i])
                {
                    indexOfDuplicate = i;
                    break;
                }
            }
            //now remove duplicate
            if (indexOfDuplicate > -1)
            {
                for (int i = diagramsToDraw.Count - 1; i > -1 && i >= indexOfDuplicate; i--)
                {
                    diagramsToDraw.RemoveAt(i);
                }
            }
            removeDuplicatesDiagramList();
        }


        public void AppendDiagram(string appendageID, string parentID, bool replaceParent)
        {
            //If this is the same as parent, forget it
            DiagramObject d = ProcedureExistsInDiagrams(appendageID);
            if (d != null)
                appendageID = d.DiagramID;
            if (diagramsToDraw.Count > 1 && diagramsToDraw[diagramsToDraw.Count - 1] == appendageID && diagramsToDraw[diagramsToDraw.Count -2] == parentID)
                return;
            PruneFlowAfterID(parentID, replaceParent);
            diagramsToDraw.Add(appendageID);
            //now remove any duplicate references b4 parentID and all parentnodes of duplicate

            //start counting here
            int indexOfDuplicate = -1;
            for (int i = diagramsToDraw.Count - 2; i > -1; i--)
            {
                if (appendageID == diagramsToDraw[i])
                {
                    indexOfDuplicate = i;
                    break;
                }
            }
            //now remove duplicate
            if (indexOfDuplicate > -1)
            {
                for (int i = indexOfDuplicate; i > -1; i--)
                {
                    diagramsToDraw.RemoveAt(i);
                }
            }
            removeDuplicatesDiagramList();
        }

        private void removeDuplicatesDiagramList()
        {
            for (int i = diagramsToDraw.Count - 1; i > -1; i--)
            {
                //Now remove duplicates
                if (diagramsToDraw.Count(d => d == diagramsToDraw[i]) > 1)
                    diagramsToDraw.RemoveAt(i);

            }
        }

        private void cleanDiagram()
        {
            for (int i = diagramsToDraw.Count - 1; i > -1; i--)
            {
                int index = diagramCache.GetFirstItemIndexByID(diagramsToDraw[i]);
                if (index < 0)
                {
                    //try and find replacement
                    for (int j = 0; j < MAXDIAGRAMS && j < diagramCache.Count; j++)
                    {
                        if (diagramCache[j].Procedures.Any(p => p.ID.Equals(diagramsToDraw[i])))
                        {
                            diagramsToDraw[i] = diagramCache[j].DiagramID;
                            break;
                        }
                    }

                }
            }
        }

        private int ProcedurePositionInDiagramsToDraw(string procedureID)
        {
            for (int i = 0; i < diagramsToDraw.Count; i++)
            {
                if (diagramsToDraw[i] == null && procedureID == null)
                    return i;
                if (diagramsToDraw[i] == null)
                    continue;
                if (diagramsToDraw[i].IndexOf(PREFIX + (procedureID as string) + SUFFIX) > -1)
                    return i;
            }
            return -1;
        }

        private void PruneFlowAfterID(string parentID, bool removeParent)
        {
            int positionOfParentToDraw = ProcedurePositionInDiagramsToDraw(parentID);
            for (int i = diagramsToDraw.Count - 1; i > positionOfParentToDraw; i--)
                diagramsToDraw.RemoveAt(i);
            if (removeParent && positionOfParentToDraw > -1)
                diagramsToDraw.RemoveAt(positionOfParentToDraw); 
        }

        private void PruneFlowAfterCurrent(bool includeParent)
        {
            if (selectedIndex > -1 && diagrams.Length > selectedIndex)
            {
                PruneFlowAfterID(diagramsToDraw[diagramsToDraw.IndexOf(diagrams[selectedIndex].DiagramID)], includeParent);
            }

        }

        public void RefreshDiagrams()
        {
            videoflow.Children.Clear();
            videoflow_reflection.Children.Clear();
            UpdateFlow();
        }

        public void ResetDiagrams()
        {
            ResetDiagrams(null);
        }

        public void ResetDiagrams(string rootProcedureID)
        {
            ClearDiagrams();
            AppendDiagram(rootProcedureID, null, false);
        }

        public RingBufferObservableWithID<DiagramObject> DiagramCache
        {
            get { return diagramCache; }
        }


        void CoverFlowControl_Loaded(object sender, RoutedEventArgs e)
        {
            
        }

        public void UpdateFlowByProcedure(string procedureID)
        {
            DiagramObject d = ProcedureExistsInDiagrams(procedureID);
            if (d != null)
                UpdateFlow(d.DiagramID);
        }

        private FrameworkElement getRootElement(FrameworkElement elm) {
            FrameworkElement parent = elm.Parent as FrameworkElement;
            if (parent != null)
                return getRootElement(parent);
            else
                return elm;
        }

        

        private double diagramWidth = 640;
        public double DiagramWidth
        {
            get { return diagramWidth; }
        }
        
        private double diagramHeight = 480;
        public double DiagramHeight
        {
            get { return diagramHeight; }
        }

        public void UpdateFlow(string diagramID) {

            //Only print the maxDiagrams
            //Build the diagram list from the diagramstodraw list
            List<DiagramObject> newList = new List<DiagramObject>();
            int selectedDiagramToDraw = diagramsToDraw.IndexOf(diagramID);
            if (selectedDiagramToDraw < 0)
                return;
            //prepend
            for (int i = selectedDiagramToDraw - 1; i >= selectedDiagramToDraw - (MAXDIAGRAMS / 2); i--)
            {
                if (i > -1 && i < diagramsToDraw.Count)
                    newList.Insert(0,diagramCache.GetFirstItemByID(diagramsToDraw[i]));
            }
            //add
            selectedIndex = newList.Count;
            newList.Add(diagramCache.GetFirstItemByID(diagramID));
            //append
            for (int i = selectedDiagramToDraw + 1; i <= selectedDiagramToDraw + (MAXDIAGRAMS / 2); i++)
            {
                if (i > -1 && i < diagramsToDraw.Count)
                    newList.Add(diagramCache.GetFirstItemByID(diagramsToDraw[i]));
            }

            //Now we have diagrams to draw in newList
            //prune anything in layout thats not in list
            for (int j = videoflow.Children.Count - 1; j > -1; j--)
            {
                if (!newList.Any(o => ((Canvas)videoflow.Children[j]).Tag == o.ID))
                {
                    videoflow.Children.RemoveAt(j);
                    videoflow_reflection.Children.RemoveAt(j);
                }
            }

            diagrams = newList.ToArray();

            UpdateFlow();
            
            if (CoverFlowChangedEvent != null)
                CoverFlowChangedEvent(this, diagramID);

        }

        public bool UpdateFlow()
        {
            if (diagrams == null)
                return false;

            string coverFlow = ControlResources.CoverFlow;
            string ref_coverflow = ControlResources.CoverFlow_Reflection;
            string n = "flow";
            double w = diagramWidth;
            double h = diagramHeight;
            string d = "0:0:0.3";

            //let's replace old diagrams (insert where required)
            for (int i = 0; i < diagrams.Length; i++)
            {
                string ni = n + diagrams[i].DiagramID;
                string tcvflow = coverFlow.Replace("{name}", ni).Replace("{d}", d).Replace("{h}", h.ToString()).Replace("{w}", w.ToString());
                string ref_tcvflow = ref_coverflow.Replace("{name}", string.Concat(ni,"_ref")).Replace("{d}", d).Replace("{h}", h.ToString()).Replace("{w}", w.ToString());
                Canvas loadedCanvas = (Canvas)XamlReader.Load(tcvflow);
                Canvas ref_loadedCanvas = (Canvas)XamlReader.Load(ref_tcvflow);
                DiagramControl dctl = new DiagramControl();
                dctl.ProcedureClicked += new DiagramControl.ProcedureClickedHandler(dctl_ProcedureClicked);
                dctl.ProcedureDoubleClicked += new DiagramControl.ProcedureDoubleClickedHandler(dctl_ProcedureDoubleClicked);
                dctl.Name = "dg";
                dctl.Width = diagramWidth;
                dctl.Height = diagramHeight;
                dctl.DiagramToDraw = diagrams[i];
                loadedCanvas.Tag = diagrams[i].ID;
                loadedCanvas.MouseLeftButtonUp += new MouseButtonEventHandler(loadedCanvas_MouseLeftButtonUp);
                loadedCanvas.Children.Add(dctl);

                DiagramControl ref_dctl = new DiagramControl();
                ref_dctl.Name = "ref_dg";
                ref_dctl.DiagramToDraw = diagrams[i];
                ref_loadedCanvas.Tag = "ref_" + diagrams[i].ID;
                dctl.DiagramReflection = ref_dctl;

                ref_loadedCanvas.Children.Add(ref_dctl);
                Canvas reflectionCover = ref_loadedCanvas.Children.First(rc => ((FrameworkElement)rc).Name == "reflectionCover") as Canvas;
                ref_loadedCanvas.Children.Remove(reflectionCover);
                ref_loadedCanvas.Children.Add(reflectionCover);

                if (!videoflow.Children.Any(c => ((Canvas)c).Tag == diagrams[i].ID))
                {
                    videoflow.Children.Insert(i, loadedCanvas);
                    videoflow_reflection.Children.Insert(i, ref_loadedCanvas);
                    //videoflow_reflection.Children.Add(new Canvas());
                }
                else
                {
                    if (true || (string)diagrams[i].ID != (((Canvas)LayoutRoot.Children[i]).Tag as string))
                    {
                        //Replace the canvas
                        UIElement elm = videoflow.Children.Single(c => (((Canvas)c).Tag as string) == (string)diagrams[i].ID);
                        int index = videoflow.Children.IndexOf(elm);
                        if (index != i)
                        {
                            videoflow.Children.RemoveAt(index);
                            videoflow.Children.Insert(i, loadedCanvas);
                            videoflow_reflection.Children.RemoveAt(index);
                            videoflow_reflection.Children.Insert(i, ref_loadedCanvas);
                        }
                    }
                }
            }

            selectRefCover(selectedIndex);
            return selectCover(selectedIndex);

            
        }

        
        void dctl_ProcedureClicked(object sender, DiagramControl.ProcedureClickedArgs e) 
        {
            if (ProcedureClicked != null) {
                ProcedureClickedArgs procArgs = new ProcedureClickedArgs(e.Diagram,e.Procedure);
                ProcedureClicked(this, procArgs);
            }
        }

        void dctl_ProcedureDoubleClicked(object sender, DiagramControl.ProcedureClickedArgs e)
        {
            if (ProcedureDoubleClicked != null)
            {
                ProcedureClickedArgs procArgs = new ProcedureClickedArgs(e.Diagram, e.Procedure);
                ProcedureDoubleClicked(this, procArgs);
            }
        }
		
        
        void loadedCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
			//UpdateFlow(int.Parse(((Canvas)sender).Tag.ToString()));
            object id = ((Canvas)sender).Tag;
            SelectCover(id);
            if (CoverFlowChangedEvent != null)
                CoverFlowChangedEvent(this, id);
        }

            

        public void SelectCover(object diagramID)
        {
            for (int i = 0; i < diagrams.Length; i++)
            {
                if (diagrams[i].DiagramID == (string)diagramID)
                {
                    selectCover(i);
                    selectRefCover(i);
                    return;
                }
            }
        }


        private bool selectCover(int index)
        {
            if (diagrams == null)
                return false;

            if (index < 0)
                index = selectedIndex;

            if (index >= diagrams.Length)
                index = diagrams.Length - 1;

            if (MenuItemClicked != null) {
                SelectedMenuItemArgs menuArgs = new SelectedMenuItemArgs(diagrams[index], index);
                MenuItemClicked(this, menuArgs);
            }
            
            
            Canvas coverflow = videoflow;
            coverflow.Width = diagramWidth;
            Storyboard sb = null;

            // Setup
            double coverwidth = diagramWidth;
            double coverwidth2 = coverwidth / 2;

            double perspectivetop = 60;
            double perspectivebottom = 10;
            double scaley = 0.60; //0.8
            double scalex = 0.6;
            //double scalex = scaley * (1.0 - perspectivetop / coverwidth);
            double offset = coverwidth * 0.60; //0.8

            // Layout
            for (var i = 0; i < coverflow.Children.Count; i++)
            {
                var cover = (FrameworkElement)coverflow.Children[i];
                
                if (cover.Name.IndexOf("_ref") > -1)
                    continue; //Move On, Do not process Reflection
                
                if (i < index)
                {
                    ((DiagramControl)cover.FindName("dg")).MoveOutOfFocus();
                    cover.SetValue(Canvas.ZIndexProperty, i);
                    sb = (Storyboard)cover.FindName(cover.Name + "_anim");
                    ((DoubleAnimation)sb.Children[0]).To = scalex;
                    ((DoubleAnimation)sb.Children[1]).To = scaley;
                    ((DoubleAnimation)sb.Children[2]).To = -perspectivebottom;
                    ((DoubleAnimation)sb.Children[3]).To = -(coverwidth - ((1.0 - scalex) * coverwidth * 0.5) + 5 + ((index - 1) - i) * scalex * coverwidth);
                    //((DoubleAnimation)sb.Children[3]).To = -(offset + ((index - 1) - i) * coverwidth2);
                    ((PointAnimation)sb.Children[4]).To = new Point(0, 0);
                    ((PointAnimation)sb.Children[5]).To = new Point(coverwidth, 0);
                    sb.Begin();
                }
                else if (i > index)
                {
                    ((DiagramControl)cover.FindName("dg")).MoveOutOfFocus();
                    var ii = (i - index - 1);
                    cover.SetValue(Canvas.ZIndexProperty, (coverflow.Children.Count - 1) - ii);
                    sb = (Storyboard)cover.FindName(cover.Name + "_anim");
                    ((DoubleAnimation)sb.Children[0]).To = scalex;
                    ((DoubleAnimation)sb.Children[1]).To = scaley;
                    ((DoubleAnimation)sb.Children[2]).To = perspectivebottom;
                    //((DoubleAnimation)sb.Children[3]).To = +(offset + ii * coverwidth2);
                    ((DoubleAnimation)sb.Children[3]).To = coverwidth - ((1.0 - scalex) * coverwidth * 0.5) + 5 + (ii * coverwidth * scalex);
                    ((PointAnimation)sb.Children[4]).To = new Point(0, 0);
                    ((PointAnimation)sb.Children[5]).To = new Point(coverwidth, 0);
                    sb.Begin();
                }
                else
                {
                    //selectedCanvasName = ((Canvas)sender).Name;
                    ((Diagram.DiagramControl)cover.FindName("dg")).MoveInFocus();
                    cover.SetValue(Canvas.ZIndexProperty, coverflow.Children.Count + 0);
                    sb = (Storyboard)cover.FindName(cover.Name + "_anim");
                    ((DoubleAnimation)sb.Children[0]).To = 1;
                    ((DoubleAnimation)sb.Children[1]).To = 1;
                    ((DoubleAnimation)sb.Children[2]).To = 0;
                    ((DoubleAnimation)sb.Children[3]).To = 0;
                    ((PointAnimation)sb.Children[4]).To = new Point(0, 0);
                    ((PointAnimation)sb.Children[5]).To = new Point(coverwidth, 0);
                    sb.Begin();

                    //zoom.ContentToZoom = cover;
                    
                }

            }
            //SelectReflection(index);
            return true;
        }


        private void selectRefCover(int index)
        {
            Canvas coverflow = videoflow_reflection;
            coverflow.Width = diagramWidth;
            Storyboard sb = null;

            // Setup
            double coverwidth = diagramWidth;
            double coverwidth2 = coverwidth / 2;

            double perspectivetop = 60;
            double perspectivebottom = 10;
            double scaley = 0.60; //0.8
            double scalex = scaley * (1.0 - perspectivetop / coverwidth);
            double offset = coverwidth * 0.60; //0.8

            // Layout
            for (var i = 0; i < coverflow.Children.Count; i++)
            {
                var cover = (FrameworkElement)coverflow.Children[i];

                if (cover.Name.IndexOf("_ref") < -1)
                    continue; //Move On, Do not process Normal

                if (i < index)
                {
                    ((DiagramControl)cover.FindName("ref_dg")).MoveOutOfFocus();
                    cover.SetValue(Canvas.ZIndexProperty, i);
                    sb = (Storyboard)cover.FindName(cover.Name + "_anim");
                    ((DoubleAnimation)sb.Children[0]).To = scalex;
                    ((DoubleAnimation)sb.Children[1]).To = scaley;
                    ((DoubleAnimation)sb.Children[2]).To = perspectivebottom;
                    ((DoubleAnimation)sb.Children[3]).To = - (offset + ((index - 1) - i) * coverwidth2);
                    ((DoubleAnimation)sb.Children[4]).To = Math.Tan(perspectivebottom)*coverwidth*scaley;
                    //((PointAnimation)sb.Children[5]).To = new Point(0, 0);
                    //((PointAnimation)sb.Children[6]).To = new Point(coverwidth, 0);
                    sb.Begin();
                }
                else if (i > index)
                {
                    ((DiagramControl)cover.FindName("ref_dg")).MoveOutOfFocus();
                    var ii = (i - index - 1);
                    cover.SetValue(Canvas.ZIndexProperty, (coverflow.Children.Count - 1) - ii);
                    sb = (Storyboard)cover.FindName(cover.Name + "_anim");
                    ((DoubleAnimation)sb.Children[0]).To = scalex;
                    ((DoubleAnimation)sb.Children[1]).To = scaley;
                    ((DoubleAnimation)sb.Children[2]).To = -perspectivebottom;
                    ((DoubleAnimation)sb.Children[3]).To = + (offset + ii * coverwidth2);
                    ((DoubleAnimation)sb.Children[4]).To = Math.Tan(perspectivebottom) * coverwidth * scaley;
                    //((PointAnimation)sb.Children[5]).To = new Point(0, 0);
                    //((PointAnimation)sb.Children[6]).To = new Point(coverwidth + coverwidth2, 0);
                    sb.Begin();
                }
                else
                {
                    ((DiagramControl)cover.FindName("ref_dg")).MoveInFocus();
                    cover.SetValue(Canvas.ZIndexProperty, coverflow.Children.Count + 0);
                    sb = (Storyboard)cover.FindName(cover.Name + "_anim");
                    ((DoubleAnimation)sb.Children[0]).To = 1;
                    ((DoubleAnimation)sb.Children[1]).To = 1;
                    ((DoubleAnimation)sb.Children[2]).To = 0;
                    ((DoubleAnimation)sb.Children[3]).To = 0;
                    ((DoubleAnimation)sb.Children[4]).To = 0;
                    //((PointAnimation)sb.Children[5]).To = new Point(0, 0);
                    //((PointAnimation)sb.Children[6]).To = new Point(coverwidth, 0);
                    sb.Begin();

                    //zoom.ContentToZoom = cover;

                }
            }
        }

       
        public class SelectedMenuItemArgs : EventArgs {
            private readonly DiagramObject m_diag;
            private readonly int m_index;

            public SelectedMenuItemArgs(DiagramObject diagram, int diagramIndex) {
                m_diag = diagram;
                m_index = diagramIndex;
            }

            public DiagramObject Diagram {
                get { return m_diag; }
            }

            public int Index {
                get { return m_index; }
            }
        }

        public class ProcedureClickedArgs : EventArgs {
            private readonly DiagramObject m_diag;

            public DiagramObject Diagram {
                get { return m_diag; }
            } 

            private readonly ProcedureObject m_proc;

            public ProcedureClickedArgs(DiagramObject diag, ProcedureObject proc) {
                m_diag = diag;
                m_proc = proc;
            }

            public ProcedureObject Procedure {
                get { return m_proc; }
            }

        }

    }
}
